﻿
/*
Editor of Supertree/TextEditor, v1.1

Author: Feng Weiguo ( 冯卫国 )
Mail: forxm@21cn.com
Web: www.supertree.org
2008-1
update 2008-9

All Rights Reserved, 2008-9

LICENSE: LGPL

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License (LGPL) as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.
*/

$esoe.$preload(
	"org.supertree.editor.TextEditor",
	function(sVarName)
	{
		if( sVarName )
		{
			this.sVarName= sVarName;
			this.PrepareEvent(sVarName);
		}
		
		this.aUndo= new Array;
		this.aOp= new Array;
		
		this.xyFind= new Array(100,50);
	},
	function()
	{
		//sub class used by this class
		function EasyDragItem()
		{
		}

		EasyDragItem.prototype={
			bDrag: 0,
			xDragScreen: 0,
			yDragScreen: 0,
			xDrag: 0,
			yDrag: 0,
			objDrag: 0,
			funcOldMouseMove: null,
			funcOldMouseUp: null,
			objSave:null,
			aSave:null,
			
			StartDrag: function(evt,obj,funcMouseMove,funcMouseUp,objSave,aSave)
			{
				if( !evt ) evt=window.event;
				
				this.bDrag=1;
				this.xDragScreen= evt.screenX;
				this.yDragScreen= evt.screenY;
				this.xDrag= parseInt(obj.style.left);
				this.yDrag= parseInt(obj.style.top);
				this.objDrag=obj;
				this.objSave=objSave;
				this.aSave=aSave;
				
				this.funcOldMouseMove= document.onmousemove;
				document.onmousemove=funcMouseMove;
				
				this.funcOldMouseUp= document.onmouseup;
				document.onmouseup=funcMouseUp;
			},
			
			OnDrag: function(evt)
			{
				if(!this.bDrag) return;
				
				if( !evt ) evt=window.event;
				this.objDrag.style.left= (this.xDrag + evt.screenX-this.xDragScreen)+"px";
				this.objDrag.style.top= (this.yDrag + evt.screenY-this.yDragScreen)+"px";
			},
			
			StopDrag: function(evt)
			{
				document.onmousemove=this.funcOldMouseMove;
				document.onmouseup=this.funcOldMouseUp;
				
				if( this.objSave )
				{
					var xy1= $esoe.$import("org.supertree.editor.TextEditor").prototype.GetAbsXY( this.objSave );
					var xy2= $esoe.$import("org.supertree.editor.TextEditor").prototype.GetAbsXY( this.objDrag );
					
					this.aSave[0]=xy2[0]-xy1[0];
					this.aSave[1]=xy2[1]-xy1[1];
				}
				
				this.bDrag=0;
			}
		};
		
		//declare start
		this.prototype= $esoe.$derive(
			"org.supertree.editor.TextParser",
			{
				_opx: function(nLine,nCol,sDel,sIns,bRaw,nMode,sKey)
				{
					this.CheckChange();
					var r= this._op(nLine,nCol,sDel,sIns,bRaw,nMode,sKey);
					this.ResetCheck();
					return r;
				},

				_opxEx: function(sel,sDel,sIns,bRaw,nMode,sKey)
				{
					this.CheckChange();
					var r= this._op(this._line(sel[0]),sel[2].length,sDel,sIns,bRaw,nMode,sKey);
					this.ResetCheck();
					return r;
				},

				PrepareEvent: function(sVarName)
				{
					//alert( this.sVarName );
					
					this.OnKeyDown= new Function("evt",sVarName+"._OnKeyDown(evt)");
					this.OnKeyPress= new Function("evt",sVarName+"._OnKeyPress(evt)");
					this.OnPaste= new Function("evt",sVarName+"._OnPaste(evt)");
					this.OnCut= new Function("evt",sVarName+"._OnCut(evt)");
					this.OnCopy= new Function("evt",sVarName+"._OnCopy(evt)");
					
					this.PreventEvent= new Function("evt",sVarName+"._PreventEvent(evt)");
					this.PreventEventEx= new Function("evt",sVarName+"._PreventEventEx(evt)");
					
					this.OnContextMenu= new Function("evt",sVarName+"._OnContextMenu(evt)");
					this.OnDragStart= new Function("evt",sVarName+"._OnDragStart(evt)");
					this.OnDragEnd= new Function("evt",sVarName+"._OnDragEnd(evt)");
					this.OnDrop= new Function("evt",sVarName+"._OnDrop(evt)");
					this.OnDragOver= new Function("evt",sVarName+"._OnDragOver(evt)");
					this.OnDragGesture= new Function("evt",sVarName+"._OnDragGesture(evt)");
					
					this.OnClick= new Function("evt",sVarName+"._OnClick(evt)");
					this.OnFrameFocus= new Function("evt",sVarName+"._OnFrameFocus(evt)");
					this.OnFrameBlur= new Function("evt",sVarName+"._OnFrameBlur(evt)");
					this.OnBlur= new Function("evt",sVarName+"._OnBlur(evt)");
					
					this.OnSelectionChange= new Function("evt",sVarName+"._OnSelectionChange(evt)");
					this.OnMouseMove= new Function("evt",sVarName+"._OnMouseMove(evt)");
					this.OnMouseDown= new Function("evt",sVarName+"._OnMouseDown(evt)");
					this.OnMouseUp= new Function("evt",sVarName+"._OnMouseUp(evt)");
					
					this.OnDragItem= new Function("evt",sVarName+".edi.OnDrag(evt)");
					this.StopDragItem= new Function("evt",sVarName+".edi.StopDrag(evt)");
					
					this.OnFind= new Function("evt",sVarName+"._OnFind(evt)");
					this.OnFindBack= new Function("evt",sVarName+"._OnFind(evt,1)");
					this.OnReplace= new Function("evt",sVarName+"._OnFind(evt,0,1)");
					
					this.StartDragFind= new Function("evt",this.sVarName+".edi.StartDrag(evt,this.parentNode.parentNode,"+this.sVarName+".OnDragItem,"+this.sVarName+".StopDragItem,document.getElementById("+this.sVarName+".sFrameID),"+this.sVarName+".xyFind)");
				},

				FindLineObj: function(obj, bug)
				{
					if(!obj) alert( bug );
					
					var oBody= this.getDocument().body;
					while( obj && obj!=oBody && obj.parentNode!=this.objTop )
					{
						obj=obj.parentNode;
					}
					return (obj && obj.parentNode==this.objTop)?obj:null;
				},

				//return array
				//[0]: start line node, [1]: end line node
				//[2]: line text before select, [3]: line text after select
				//[4]: selected text length
				GetSelection: function()
				{
					//this.TRACE("GetSelection()");
					
					var r,r1,r2,obj1,obj2,txt1,txt2,nLen;
					if( window.ActiveXObject )
					{
						//alert(this.sVarName);
						r= this.getDocument().selection.createRange();
						nLen=r.text.length;
						r1=r.duplicate();
						r2=r.duplicate();
						r1.collapse(true);
						r2.collapse(false);
						obj1= this.FindLineObj(r1.parentElement());
						obj2= this.FindLineObj(r2.parentElement());
						
						if(!obj1) return null;
						
						r.moveToElementText(obj1);
						r.setEndPoint("EndToEnd",r1);
						txt1=r.text;
						r.moveToElementText(obj2);
						r.setEndPoint("StartToStart",r2);
						txt2=r.text;
					}
					else
					{
						var ws= this.getWindow().getSelection();
						if(ws && ws.rangeCount>0 ) r= ws.getRangeAt(0);
						else
						{
							//alert("sel=null");
							return null;
						}
						
						nLen=r.toString().length;
						//obj1 & obj2
						//obj1= this.FindLineObj( r.startContainer );
						obj1=r.startContainer;
						if( obj1.nodeType==3 ) obj1= this.FindLineObj( obj1, 1 );
						else
						{
							if( r.startOffset==0 )
							{
								obj1= this.FindLineObj( (obj1.childNodes && obj1.childNodes.length>0)?obj1.childNodes[0]:obj1, 2 );
							}
							else
							{
								obj1= this.FindLineObj( obj1.childNodes[r.startOffset-1], "2b" );
							}
						}
						//alert("obj1");
						
						obj2=r.endContainer;
						if( obj2.nodeType==3 ) obj2= this.FindLineObj( obj2, 3 );
						else
						{
							//alert( r.endOffset +"\n==\n"+r.endContainer.textContent+"\n==\n"+r.endContainer.childNodes[r.endOffset].textContent );
							if( r.endOffset==0 )
							{
								//alert( obj2.previousSibling );
								obj2= this.FindLineObj( (obj2.childNodes && obj2.childNodes.length>0)?obj2.childNodes[0]:obj2, 4 );
							}
							else
							{
								obj2= this.FindLineObj( obj2.childNodes[r.endOffset-1], 5 );
							}
						}
						//alert("obj2");

						if( !obj1 || !obj2 )
						{
							throw( "fail to get selection" );
						}
						
						//alert(obj2+"\n==\n"+obj2+"\n==\n"+obj2.innerHTML+"\n==\n"+obj2.textContent);
						
						//txt1
						if( obj1 )
						{
							r1=r.cloneRange();
							r1.collapse(true);
							r1.setStartBefore(obj1);
							txt1=r1.toString();
						}
						else txt1="";
						
						//txt2
						if(obj2)
						{
							r1=r.cloneRange();
							r1.collapse(false);
							r1.setEndAfter(obj2);
							txt2=r1.toString();
						}
						else txt2="";
					}
					return [obj1,obj2,txt1,txt2,nLen ];
				},

				//ff bug, caret disappear
				ClearSelection_mz: function()
				{
					if( !window.ActiveXObject ) this.getWindow().getSelection().removeAllRanges();
				},

				// return [obj,idx], if idx=-1, the end of obj
				GetRangeDetail_mz: function( obj, idx )
				{
					var i,imax,len;
					imax=obj.childNodes.length;
					var oWin=this.getWindow();
					for( i=0;i<imax;i++)
					{
						len=obj.childNodes[i].textContent.length;
						if( len>idx )
						{
							obj=obj.childNodes[i];
							if( obj.nodeType==3 ) return [obj,idx];	//text
							else return this.GetRangeDetail_mz( obj, idx );
						}
						else if( len==idx )
						{
							return [obj.childNodes[i],-1];
						}
						else idx-=len;
					}
					return [obj,-1];
				},

				SetCaret: function( objLine, idx )
				{
					//this.CheckChange();	//before SetCaret, may always be _op; if not, enable this line.
					//this.ResetCheck();
					
					var r;
					var oWin=this.getWindow();
					if( window.ActiveXObject )
					{
						r= oWin.document.body.createTextRange();
						r.moveToElementText(objLine);
						r.moveStart( "character", idx );
						r.collapse(true);
						r.select();
					}
					else
					{
						r= oWin.document.createRange();
						r.selectNodeContents(objLine);
						if(idx<1) r.collapse(true);
						else
						{
							//r= oWin.document.createRange();
							var d=this.GetRangeDetail_mz(objLine,idx);
							//alert(d);
							//r.selectNode(d[0]);
							if( d[1]!=-1 ) r.setEnd( d[0],d[1]);
							else r.setEndAfter( d[0] );
							r.collapse(false);
						}
						oWin.getSelection().removeAllRanges();
						oWin.getSelection().addRange(r);
						this.Focus();
					}
					//if(!window.ActiveXObject) this.PrepareCheck();
					this.PrepareCheckEx();
				},

				SetSelection: function(sel)
				{
					//this.CheckChange();

					var oWin=this.getWindow();
					if( window.ActiveXObject )
					{
						var r= oWin.document.body.createTextRange();
						r.moveToElementText(sel[0]);
						r.moveStart( "character", sel[2].length );
						var r2=r.duplicate();
						r2.moveToElementText(sel[1]);
						r2.moveStart( "character", sel[1].innerText.length-sel[3].length );
						r.setEndPoint( "EndToStart", r2 );
						r.select();
					}
					else
					{
						var r= oWin.document.createRange();
						
						var d= this.GetRangeDetail_mz(sel[0],sel[2].length);
						if( d[1]!=-1 ) r.setStart( d[0],d[1]);
						else r.setStartAfter( d[0] );
						
						d= this.GetRangeDetail_mz(sel[1],sel[1].textContent.length-sel[3].length);
						if( d[1]!=-1 ) r.setEnd( d[0],d[1]);
						else r.setEndAfter( d[0] );
						
						oWin.getSelection().removeAllRanges();
						oWin.getSelection().addRange(r);
						this.Focus();
					}
					this.PrepareCheckEx();
				},

				DeleteSelection: function(sel,bNoCaret)
				{
					this.ClearSelection_mz();
					
					var re= this._opxEx( sel, this.GetSelectionText(sel), "" );
					this.UpdateLine(sel[0]);
					
					if( re && !bNoCaret ) this.SetCaret( sel[0], sel[2].length );
				},


				regHeadSpace: new RegExp,

				TabSelection: function( sel, bShift )
				{
					var obj=sel[0];
					var i,s,s2;
					
					var nM= ( sel[0]!=sel[1])?1:0;
					
					while(1)
					{
						//this.ResetCheck();
						s=this.getInnerText(obj);
						i=s.replace( this.regHeadSpace , "$1" ).length;
						if( bShift )
						{
							if(i>0)
							{
								i= i%4;
								if(i==0)i=4;
								
								if( nM==1 ) { this._saveop( ["_head"] ); nM=2; }
								
								this._opx( this._line(obj), 0, s.substring(0,i),"");
								this.UpdateLine(obj);
							}
						}
						else
						{
							i= 4-(i%4);
							s2=" "+((i>1)?" ":"")+((i>2)?" ":"")+((i>3)?" ":"");

							if( nM==1 ) { this._saveop( ["_head"] ); nM=2; }

							this._opx( this._line(obj), 0, "",s2);
							this.UpdateLine(obj);
						}
						
						if( obj==sel[1] ) break;
						obj=obj.nextSibling;
					}
					
					if( nM==2 ) this._saveop( ["_end"] );
					
					var r1,r2;
					var oWin= this.getWindow();
					if( window.ActiveXObject )
					{
						r1=oWin.document.body.createTextRange();
						r1.moveToElementText(sel[0]);
						if( sel[0]!=sel[1])
						{
							r2=r1.duplicate();
							r2.moveToElementText(sel[1]);
							r1.setEndPoint( "EndToEnd", r2 );
						}
						r1.select();
					}
					else
					{
						r1=oWin.document.createRange();
						r1.selectNodeContents(sel[0]);
						if( sel[0]!=sel[1])
						{
							obj=sel[1];
							while( (!obj.childNodes || obj.childNodes.length<1) && obj!=sel[0] )
							{
								obj=obj.previousSibling;
							}
							if( sel[0]!=obj)
							{
								r1.setEndAfter( obj.childNodes[obj.childNodes.length-1] );
							}
						}
						oWin.getSelection().removeAllRanges();
						oWin.getSelection().addRange(r1);
					}
					//this.ResetCheck();
				},

				/*
				RefreshSelection: function()
				{
					var sel,s;
					
					//try
					{
						//this._OnSelectionChange();
						this.CheckChange();
						
						sel= this.GetSelection();
						
						if( sel[0]==sel[1])
						{
							this.UpdateLine(sel[0]);
							this.SetCaret(sel[0],sel[2].length);
						}
					}
					//catch(e){ alert(e+","+e.description); }
				},
				*/

				_OnKeyDown: function( evt )
				{
					var oWin= this.getWindow();
					if( !evt ) evt=oWin.event;

					var bRet=true;
					var r,nd,oLine,r2,i,s,j,nd,obj;
					var sel;

					//this.TRACE( evt.keyCode );
					
					//try
					{
						//window.setTimeout( this.sVarName+".OnSelectionChange();", 0 );

						if( evt.keyCode==13 )	//enter
						{
							sel= this.GetSelection();
							
							this.ClearSelection_mz();
							
							//prefix tab
							var sTab= sel[2].replace( this.regHeadSpace , "$1" );
							if( sTab.length>3) sTab=sTab.substring(0,sTab.length-(sTab.length%4));
							else sTab="";
							
							this._opxEx( sel, this.GetSelectionText(sel), "\n"+sTab );
							sel[0].setAttribute("Stack","");
							this.UpdateLine(sel[0]);
							this.SetCaret( sel[0].nextSibling, sTab.length );
							bRet=false;
						}
						else if( evt.keyCode==8 )	//backspace
						{
							sel= this.GetSelection();
							
							if( sel[0]!=sel[1] || sel[4]>0)
							{
								this.DeleteSelection(sel);
							}
							else
							{
								if( sel[2]=="" )
								{
									obj= sel[0].previousSibling;
									if( obj )
									{
										this.ClearSelection_mz();
										
										s=this.getInnerText(obj);
										this._opx(this._line(obj),s.length,"\n","");
										this.UpdateLine( obj );
										this.SetCaret( obj, s.length );
									}
								}
								else
								{
									this.ClearSelection_mz();
									
									if( evt.ctrlKey )
									{
										s=sel[2].replace( /(^|^.+\b)((.\B)*.)$/ , "$2");
										this._opx( this._line(sel[0]), sel[2].length-s.length, s, "" );
									}
									else
									{
										var sTab= sel[2].replace( this.regHeadSpace , "$1" );
										if(sTab && sTab==sel[2] )
										{
											//use backspace to delete TAB
											i=sel[2].length%4;
											if(i==0)i=4;
											s=sel[2].substring(sel[2].length-i,sel[2].length);
										}
										else
										{
											s=sel[2].substring(sel[2].length-1,sel[2].length);
										}
										
										this._opx( this._line(sel[0]), sel[2].length-s.length, s, "",0,0,"BS" );
									}
									
									this.UpdateLine(sel[0]);
									this.SetCaret( sel[0], sel[2].length-s.length );
								}
							}
							
							bRet=false;
						}
						else if( evt.keyCode==46 )	//del
						{
							sel= this.GetSelection();
							
							if( sel[0]!=sel[1] || sel[4]>0 )
							{
								this.DeleteSelection(sel);
							}
							else
							{
								if( sel[3]=="" )
								{
									if( sel[0].nextSibling )
									{
										this._opxEx( sel, "\n", "" );
										this.UpdateLine( sel[0] );
										this.SetCaret(sel[0],sel[2].length);
									}
								}
								else
								{
									if( evt.ctrlKey )
									{
										this._opxEx( sel, sel[3].replace( /^(.(\B.)*)(\b.+$|$)/ , "$1" ), "" );
									}
									else
									{
										this._opxEx( sel, sel[3].substring(0,1) , "",0,0,"Del" );
									}
									this.UpdateLine( sel[0] );
									this.SetCaret(sel[0],sel[2].length);
								}
							}
							
							bRet=false;
						}
						else if( evt.keyCode==9 )	//tab
						{
							sel= this.GetSelection();
							
							if( sel[4]>0 )
							{
								this.TabSelection(sel,evt.shiftKey);
							}
							else
							{
								if( evt.shiftKey )
								{
									i= sel[2].length;
									j= i%4;
									if( j!=0 ) this.SetCaret(sel[0],i-j);
									else if( i>0 ) this.SetCaret(sel[0],i-4);
									else
									{
										s= sel[3].replace( this.regHeadSpace ,"$1");
										if( s.length>4) s=s.substring(0,4);
										if(s)
										{
											this._opxEx( sel, s, "" );
											this.UpdateLine( sel[0] );
											this.SetCaret(sel[0],0);
										}
									}
								}
								else
								{
									s= sel[3].replace( this.regHeadSpace ,"$1");
									j=s.length%4;
									
									i=4-(sel[2].length%4);
									s=" "+((i>1)?" ":"")+((i>2)?" ":"")+((i>3)?" ":"");
									
									this._opxEx( sel, sel[3].substring(0,j), s );
									this.UpdateLine( sel[0] );
									this.SetCaret(sel[0],sel[2].length+i);
								}
							}
							
							bRet=false;
						}
						else if( evt.keyCode==88 )		// Ctrl+X for firefox
						{
							if( evt.ctrlKey )
							{
								if( !window.ActiveXObject )
								{
									window.setTimeout( this.sVarName+".CutData()", 0 );
									bRet=false;
								}
							}
						}
						else if( evt.keyCode==86 )		// Ctrl+V for firefox
						{
							if( evt.ctrlKey )
							{
								if( !window.ActiveXObject )
								{
									window.setTimeout( this.sVarName+".PasteData()", 0 );
									bRet=false;
								}
							}
						}
						else if( evt.keyCode==67 )		// Ctrl+C for firefox
						{
							if( evt.ctrlKey )
							{
								if( !window.ActiveXObject )
								{
									window.setTimeout( this.sVarName+".CopyData()", 0 );
									bRet=false;
								}
							}
						}
						else if( evt.keyCode==65 )		// Ctrl+A for firefox
						{
							if( evt.ctrlKey )
							{
								if( !window.ActiveXObject )
								{
									window.setTimeout( this.sVarName+".SelectAll()", 0 );
									bRet=false;
								}
							}
						}
						else if( evt.keyCode==70 )		// Ctrl+F
						{
							if( evt.ctrlKey )
							{
								window.setTimeout( this.sVarName+".Find()", 0 );
								bRet=false;
							}
						}
						else if( evt.keyCode==71 )		// Ctrl+G
						{
							if( evt.ctrlKey )
							{
								window.setTimeout( this.sVarName+".GotoLine()", 0 );
								bRet=false;
							}
						}
						else if( evt.keyCode==90 )		// Ctrl+Z
						{
							if( evt.ctrlKey )
							{
								window.setTimeout( this.sVarName+".Undo()", 0 );
								bRet=false;
							}
						}
						else if( evt.keyCode==89 )		// Ctrl+Y
						{
							if( evt.ctrlKey )
							{
								window.setTimeout( this.sVarName+".Redo()", 0 );
								bRet=false;
							}
						}
						else if( evt.keyCode==27 )		// ESC
						{
							window.setTimeout(this.sVarName+".CancelOperate()",0);
						}
						else if(evt.keyCode==37||evt.keyCode==38||evt.keyCode==39||evt.keyCode==40)	//l/t/r/b
						{
							window.setTimeout(this.sVarName+".CheckChange();"+this.sVarName+".UpdateXY()",0);
						}
						else if( evt.keyCode==33 )	//page up
						{
							if( window.ActiveXObject )
							{
								this.PageDown(1);
								bRet=false;
							}
						}
						else if( evt.keyCode==34 )	//page down, ie only
						{
							if( window.ActiveXObject )
							{
								this.PageDown();
								bRet=false;
							}
						}
						else if( evt.keyCode==36 )	//home
						{
							if( !evt.ctrlKey && !evt.shiftKey && !evt.altKey )
							{
								window.setTimeout(this.sVarName+".OnHomeKey()",0);
								bRet=false;
							}
						}
						else if( evt.keyCode==114 )	//F3
						{
							if( !evt.shiftKey )
							{
								window.setTimeout(this.sVarName+"._OnFind()",0);
							}
							else
							{
								window.setTimeout(this.sVarName+"._OnFind(null,1)",0);
							}
							bRet=false;
						}
						else if( evt.keyCode==112 )	//F1
						{
							window.setTimeout(this.sVarName+".ShowHelp()",0);
							bRet=false;
						}
						else if( evt.keyCode==113 )	//F2
						{
							if( evt.ctrlKey )
							{
								window.setTimeout(this.sVarName+".ToggleBookmark()",0);
							}
							else if(evt.shiftKey)
							{
								window.setTimeout(this.sVarName+".NextBookmark(1)",0);
							}
							else
							{
								window.setTimeout(this.sVarName+".NextBookmark()",0);
							}
							bRet=false;
						}
					}
					//catch(e){ alert(e+","+e.description); bRet=false; }
					
					if( !bRet )
					{
						if(!window.ActiveXObject) this.UpdateXY();
						return this._PreventEvent(evt);
					}
					else
					{
						//this.CheckChange();
						return bRet;
					}
				},

				OnHomeKey: function()
				{
					var sel=this.GetSelection();
					if(!sel || !sel[0] ) return;
					var s;
					if(sel[2]=="")
					{
						s= sel[3].replace( this.regHeadSpace , "$1" );
						if(s) this.SetCaret(sel[0],s.length);
					}
					else
					{
						s= sel[2].replace( this.regHeadSpace , "$1" );
						if(s!=sel[2])
						{
							this.SetCaret(sel[0],s.length);
						}
						else
						{
							s+= sel[3].replace( this.regHeadSpace , "$1" );
							this.SetCaret(sel[0],(s==sel[2])?0:s.length);
						}
					}
				},

				GotoLine: function()
				{
					var sel=this.GetSelection();
					var n="";
					if(sel && sel[0]) n=this._line(sel[0])+1;
					
					n= this.getWindow().prompt("Input the line number to jump to:",n);
					if(!n) return;
					
					if(n<1 || n>this.objTop.childNodes.length) return;
					
					var obj= this.objTop.childNodes[n-1];
					
					this.SetCaret(obj,0);
					
					if(!window.ActiveXObject)
					{
						obj.scrollIntoView();
					}
				},

				PageDown: function(bUp)
				{
					var objFrm= document.getElementById(this.sFrameID);
					var h=objFrm.offsetHeight;
					var sel=this.GetSelection();
					var obj=sel[0],obj2;
					
					var h0=bUp?0:obj.offsetHeight;
					while(obj2=bUp?obj.previousSibling:obj.nextSibling)
					{
						obj=obj2;
						h0+=obj.offsetHeight;
						if(h0>h) break;
					}
					this.SetCaret(obj,sel[2].length);
				},

				//return [left-same-string-number,right-same-string-number], if same return null.
				FindDiff: function(str1,str2)
				{
					var i;
					var imax=str1.length;
					for(i=0;i<imax;i++) { if( str1.charAt(i)!=str2.charAt(i) ) break; }
					if( i==imax)
					{
						if(str2.length==imax) return null;
						//alert(str1+"\n"+str2+"\n"+str1.length+","+str2.length+","+imax+","+0);
						return [imax,0];
					}
					
					var j1=str1.length-1;
					var j2=str2.length-1;
					var j=0;
					while(i<=j1 && i<=j2)
					{
						if( str1.charAt(j1)!=str2.charAt(j2) ) break;
						j++; j1--; j2--;
					}
					//alert(str1+"\n"+str2+"\n"+str1.length+","+str2.length+","+i+","+j);
					return [i,j];
				},

				//return [top-same-line-number,left-same-string-number,bottom-same-line-number,right-same-string-number,strDiff1,strDiff2],
				//if same return null.
				FindDiffEx: function(sa1,sa2)
				{
					//from start
					var i;
					var imax1=sa1.length;
					var imax2=sa2.length;
					for(i=0;i<imax1 && i<imax2;i++) { if( sa1[i]!=sa2[i] ) break; }

					if( i==imax1 )
					{
						if(imax1==imax2) return null;
						return [i,0,0,0,"",sa2.slice(i,sa2.length).join("\n")+"\n"];
					}
					else if(i==imax2)
					{
						return [i,0,0,0,sa1.slice(i,sa1.length).join("\n")+"\n",""];
					}
					
					//from end
					var j1=imax1-1;
					var j2=imax2-1;
					var j=0;
					while(i<=j1 && i<=j2)
					{
						if( sa1[j1]!=sa2[j2] ) break;
						j++; j1--; j2--;
					}
					
					if( (i+j)==imax1 )
					{
						return [i,0,j,0,"",sa2.slice(i,imax2-j).join("\n")+"\n"];
					}
					else if( (i+j)==imax2 )
					{
						return [i,0,j,0,sa1.slice(i,imax1-j).join("\n")+"\n",""];
					}
					else
					{
						//first diff line
						var d1;
						var jmax=sa1[i].length;
						for(d1=0;d1<jmax;d1++) { if( sa1[i].charAt(d1)!=sa2[i].charAt(d1) ) break; }
						
						//last diff line
						j1=imax1-j-1;
						j2=imax2-j-1;
						var k1=sa1[j1].length-1;
						var k2=sa2[j2].length-1;
						var d2=0;
						while( k1>=0 && k2>=0 )
						{
							if( sa1[j1].charAt(k1)!=sa2[j2].charAt(k2) ) break;
							k1--; k2--; d2++;
						}
						
						var s1,s2;
						//s1
						if( i==j1) s1= sa1[i].substring( d1, sa1[i].length-d2 );
						else if( i+1==j1 ) s1= sa1[i].substring( d1, sa1[i].length )+"\n"+sa1[j1].substring( 0, sa1[j1].length-d2 );
						else s1= sa1[i].substring( d1, sa1[i].length )+"\n"+sa1.slice(i+1,j1).join("\n")+"\n"+sa1[j1].substring( 0, sa1[j1].length-d2 );
						//s2
						if( i==j2) s2= sa2[i].substring( d1, sa2[i].length-d2 );
						else if( i+1==j2 ) s2= sa2[i].substring( d1, sa2[i].length )+"\n"+sa2[j2].substring( 0, sa2[j2].length-d2 );
						else s2= sa2[i].substring( d1, sa2[i].length )+"\n"+sa2.slice(i+1,j2).join("\n")+"\n"+sa2[j2].substring( 0, sa2[j2].length-d2 );
						
						return [i,d1,j,d2,s1,s2];
					}
					
				},

				_OnKeyPress: function( evt )
				{
					if( !evt ) evt=this.getWindow().event;

					var c= (window.ActiveXObject)?evt.keyCode:evt.charCode;
					//var s=String.fromCharCode(c);
					//this.TRACE( c+","+s+","+s.length+","+evt.isChar );
					
					//this.CheckChange();
					
					/*
					*/
					if( c>=32 && c<=126 && !evt.ctrlKey && !evt.altKey )	//all printable keyboard chars
					{
						//window.setTimeout(this.sVarName+".CheckChange()",0);
						var sel= this.GetSelection();
						var sDel= this.GetSelectionText(sel);
						//alert(sDel);
						/*
						*/
						this._opxEx(sel,sDel,String.fromCharCode(c),0,0,"Inp");
						this.UpdateLine(sel[0]);
						this.SetCaret( sel[0], sel[2].length+1 );
						
						if(!window.ActiveXObject) this._OnSelectionChange();
						
						return this._PreventEvent(evt);
					}
					
					//window.setTimeout(this.sVarName+".RefreshSelection()",0);
					//window.setTimeout(this.sVarName+".CheckChange()",0);
					
					//return this._PreventEvent(evt);
				},

				Undo: function()
				{
					this.CheckChange();
					
					if(this.nOpIdx<1 ) return;
					//alert( this.nOpIdx+"\n"+"["+this.aOp.join("]\n[")+"]" );

					this.nOpIdx-=1;
					
					//alert(this.nOpIdx);
					var a=this.aOp[this.nOpIdx];
					if(a[0] && a[0].charAt(0)!="_" )a[0]="";	//stop continous op
					
					this.ClearSelection_mz();
					//alert(a);
					
					var re,aChk,obj;
					if( a[0]=="_end" )
					{
						while(1)
						{
							if(this.nOpIdx<1 ) return;
							this.nOpIdx-=1;
							aChk=this.aOp[this.nOpIdx];
							if(aChk[0]=="_head") break;
							else if(aChk[0]=="_end") { this._op_fail("group fail"); return; }
							
							a=aChk;
							re= this._opx( a[1], a[2], a[4], a[3], 0, 1 );
							if(re)
							{
								obj=this.objTop.childNodes[a[1]];
								obj.setAttribute("Stack","");
								this.UpdateLine( obj );
							}
						}
					}
					else if(a[0]=="_head") { this._op_fail("group fail"); return; }
					else
					{
						re= this._opx( a[1], a[2], a[4], a[3], 0, 1 );
						if(re)
						{
							obj=this.objTop.childNodes[a[1]];
							obj.setAttribute("Stack","");
							this.UpdateLine( obj );
						}
					}
					
					if(re)
					{
				//		this.UpdateLine( this.objTop.childNodes[re[0]] );
				//		var obj=this.objTop.childNodes[a[1]];
				//		obj.setAttribute("Stack","");
				//		this.UpdateLine( obj );
						this.SetCaret( this.objTop.childNodes[re[0]], re[1] );
					}
				},

				Redo: function()
				{
					if(this.nOpIdx>=this.aOp.length ) return;
					this.nOpIdx+=1;
					
					var a=this.aOp[this.nOpIdx-1];
					if(a[0] && a[0].charAt(0)!="_" )a[0]="";	//stop continous op
					
					this.ClearSelection_mz();
					
					var re,aChk;
					var obj;
					if( a[0]=="_head" )
					{
						while(1)
						{
							if(this.nOpIdx>=this.aOp.length ) return;
							this.nOpIdx+=1;
							aChk=this.aOp[this.nOpIdx-1];
							if(aChk[0]=="_end") break;
							else if(aChk[0]=="_head") { this._op_fail("group fail"); return; }
							
							a=aChk;
							re= this._opx( a[1], a[2], a[3], a[4], 0, 1 );
							//if(!obj1) obj1= this.objTop.childNodes[a[1]];
							//this.objTop.childNodes[a[1]].setAttribute("Stack","");
							if(re)
							{
								obj=this.objTop.childNodes[a[1]];
								obj.setAttribute("Stack","");
								this.UpdateLine( obj );
							}
						}
					}
					else if(a[0]=="_end") { this._op_fail("group fail"); return; }
					else
					{
						re= this._opx( a[1], a[2], a[3], a[4], 0, 1 );
						if(re)
						{
							obj=this.objTop.childNodes[a[1]];
							obj.setAttribute("Stack","");
							this.UpdateLine( obj );
						}
					}
					
					if(re)
					{
				//		this.UpdateLine( this.objTop.childNodes[re[0]] );
				//		var obj=obj1?obj1:this.objTop.childNodes[a[1]];
				//		obj.setAttribute("Stack","");
				//		this.UpdateLine( obj );
						this.SetCaret( this.objTop.childNodes[re[0]], re[1] );
					}
				},

				_PreventEvent: function( evt )
				{
					if( !evt ) evt=this.getWindow().event;
					
					evt.returnValue= false;
					evt.cancelBubble= true;
					
					try{evt.keyCode=0;}catch(e){}
					
					if(evt.preventDefault) evt.preventDefault();
					
					return false;
				},

				PasteData: function()
				{
					var s= this.getClipboard();
					if( !s ) return;
					
					//delete
					this.RestoreSelection();	//for ie6
					
					var sel= this.GetSelection();
					
					var re= this._opx( this._line(sel[0]), sel[2].length, this.GetSelectionText(sel), s, 1 );
					sel[0].setAttribute("Stack","");
					this.UpdateLine(sel[0]);
					if(re) this.SetCaret( this.objTop.childNodes[re[0]], re[1] );
					
					this.Focus();
					
					this.PrepareCheckEx();
				},

				//if saText is undefined, return text; else saText should be array object
				GetSelectionText: function(sel, saText )
				{
					//if(!sel[0]) alert(this.GetSelectionText.caller);
					
					var s;
					if( sel[0]==sel[1] )
					{
						s= this.getInnerText(sel[0]);
						s= s.substring( sel[2].length,s.length-sel[3].length );
						if( saText ){ saText[0]=s; return; }
						else return s;
					}
					
					//first
					var sa= saText?saText:(new Array);
					
					s= this.getInnerText(sel[0]);
					sa.push(s.substring( sel[2].length,s.length));
					
					//middle
					var obj=sel[0];
					var obj2=sel[1];
					if(window.ActiveXObject )
					{
						while( (obj=obj.nextSibling)!=obj2) { sa.push(obj.innerText); }
					}
					else
					{
						while( (obj=obj.nextSibling)!=obj2) { sa.push(obj.textContent.replace( this.regDelR, "" ).replace( this.regSp, " " )); }
					}
					
					//last
					s=this.getInnerText(sel[1]);
					sa.push(s.substring(0,s.length-sel[3].length));
					if( !saText) return sa.join("\n");
				},

				CutData: function()
				{
					var sel=this.GetSelection();
					if( sel[4]<1 ) return;
					
					//alert(this.GetSelectionText(sel));
					this.setClipboard(this.GetSelectionText(sel));
					this.DeleteSelection(sel);
					
					this.Focus();
				},

				Focus: function()
				{
					document.getElementById(this.sFrameID).contentWindow.focus();
				},

				GetAbsXY: function(obj)
				{
					var x=0;
					var y=0;
					while( obj )
					{
						x+=obj.offsetLeft;
						y+=obj.offsetTop;
						obj=obj.offsetParent;
					}
					return [x,y];
				},

				ContextMenuItem: function( name, bDisabled, sEval, bSplit, sTitle, bPart )
				{
					return "<div style=\""+(window.ActiveXObject?"*width:100%;":"")+"cursor:default;"+(bPart?"":"padding-left:5px;padding-right:5px;")+"padding-top:1px;padding-bottom:1px;color:"+(bDisabled?"gray":"blue")+(bSplit?";border-top:1px solid lightgrey":"")+";\" "+
						"onmouseover=\"this.style.backgroundColor='darkblue';this.style.color='"+(bDisabled?"gray":"white")+"'\" "+
						"onmouseout=\"this.style.backgroundColor='white';this.style.color='"+(bDisabled?"gray":"blue")+"'\" "+
						//"onclick=\""+this.sVarName+".SaveSelection();"+this.sVarName+".RestoreSelection();"+(bDisabled?"":sEval)+";\" "+
						"onmousedown=\""+this.sVarName+".SaveSelection();"+this.sVarName+".RestoreSelection();"+"return "+this.sVarName+"._PreventEventEx(arguments[0])\" "+
						"onmouseup=\""+this.sVarName+".RestoreSelection();"+(bDisabled?"":sEval)+";return "+this.sVarName+"._PreventEventEx(arguments[0])\" "+
						//"onmouseup=\""+this.sVarName+".RestoreSelection();return "+this.sVarName+"._PreventEventEx(arguments[0])\" "+
						(sTitle?("title=\""+sTitle+"\""):"")+"><nobr>"+
						name+
						"</nobr></div>";
				},

				nItemZindexBase: 90000,	//for esoe dom
				
				ShowContextMenu: function( clientX, clientY, bCreateOnly )
				{
					//alert(clientX+","+clientY);
					
					var sel=this.GetSelection();
					//if(!sel) return;

					var xy= this.GetAbsXY( document.getElementById(this.sFrameID) );
					
					var obj= document.getElementById("ctxmenu_SupertreeTextEditor");
					if(!obj)
					{
						obj= document.body.appendChild(document.createElement("div"));
						obj.id="ctxmenu_SupertreeTextEditor";
						obj.style.position="absolute";
						obj.style.display="none";
						obj.style.border="1px solid blue";
						obj.style.backgroundColor="white";
						//obj.style.height="1em";
						obj.style.padding="5px";
						obj.style.fontSize="9pt";
						obj.style.fontFamily="'宋体','Courier New'";
						//obj.style.zIndex=1000;
						//obj.style.zIndex=90000;	//for esoe dom
						obj.style.zIndex=this.nItemZindexBase;
						
						obj.onmousedown=this.OnFrameBlur;
						obj.onmouseup=this.OnFrameFocus;
						//obj.onmousedown=this.PreventEventEx;
						//obj.onmouseup=this.PreventEventEx;

					}
					if(bCreateOnly) return;
					
					obj.style.width="6em";
					
					var sa=new Array;
					sa[sa.length]=this.ContextMenuItem( "Cut", sel[4]?0:1, this.sVarName+".HideContextMenu();"+this.sVarName+".CutData();" );
					sa[sa.length]=this.ContextMenuItem( "Copy", sel[4]?0:1, this.sVarName+".HideContextMenu();"+this.sVarName+".CopyData();" );
					sa[sa.length]=this.ContextMenuItem( "Paste", 0, this.sVarName+".HideContextMenu();"+this.sVarName+".PasteData();" );
					sa[sa.length]=this.ContextMenuItem( "Select All", 0, this.sVarName+".HideContextMenu();"+this.sVarName+".SelectAll();",1);
					
					//alert(sa.join(""));
					obj.innerHTML=sa.join("");

					obj.style.left=(parseInt(xy[0])+parseInt(clientX)+9)+"px";
					obj.style.top= (parseInt(xy[1])+parseInt(clientY))+"px";
					obj.style.display="block";
					
				},

				HideContextMenu: function()
				{
					var obj= document.getElementById("ctxmenu_SupertreeTextEditor");
					if(obj ) obj.style.display="none";
				},

				xyFind: null,

				edi: new EasyDragItem,

				_PreventEventEx: function(evt)
				{
					evt=evt?evt:window.event;
					var srcElement= window.ActiveXObject?evt.srcElement:evt.target;

					if( srcElement.tagName.toUpperCase()=="TEXTAREA" ) return true;
					else if( srcElement.tagName.toUpperCase()=="INPUT" )
					{
						if( srcElement.type=="text" || srcElement.type=="password" ) return true;
					}
					else if( srcElement.tagName.toUpperCase()=="IMG")
					{
						return true;
					}
					
					evt.returnValue= false;
					evt.cancelBubble= true;
					
					if(evt.preventDefault) evt.preventDefault();

					return false;
				},

				_FindVar: "",	//static, access by SupertreeTextEditor.prototype._FindVar

				_CloseFind: function()
				{
					var obj= document.getElementById("dlgfind_SupertreeTextEditor");
					if(obj) obj.style.display="none";
					eval("window."+$esoe.$import("org.supertree.editor.TextEditor").prototype._FindVar+".OnFrameFocus()");
				},

				Find: function()
				{
					var obj= document.getElementById("dlgfind_SupertreeTextEditor");
					if(!obj)
					{
						obj= document.body.appendChild(document.createElement("div"));
						obj.id="dlgfind_SupertreeTextEditor";
						obj.style.position="absolute";
						obj.style.display="none";
						obj.style.border="1px solid blue";
						obj.style.backgroundColor="white";
						obj.style.width="20em";
						//obj.style.height="1em";
						//obj.style.padding="5px";
						obj.style.fontSize="9pt";
						obj.style.fontFamily="'宋体','Courier New'";
						//obj.style.zIndex=1001;
						//obj.style.zIndex=90001;	//for esoe dom
						obj.style.zIndex= this.nItemZindexBase+1;
						
						obj.style.cursor="default";
						
						obj.innerHTML="<div style='background:gray;color:white;width:100%;font-weight:bold;'>"+
								"<div onmouseover=\"this.style.color='blue';\" onmouseout=\"this.style.color='white';\" style='float:right;font-weight:normal;cursor:pointer;' title='Close' onclick=\"$esoe.$import('org.supertree.editor.TextEditor').prototype._CloseFind();\">[X]</div>"+
								//"<div style='cursor:move;' onmousedown=\""+this.sVarName+".edi.StartDrag(arguments[0],this.parentNode.parentNode,"+this.sVarName+".OnDragItem,"+this.sVarName+".StopDragItem,document.getElementById("+this.sVarName+".sFrameID),"+this.sVarName+".xyFind)\">Find/Replace</div>"+
								"<div style='cursor:move;' id='dragFind_SupertreeTextEditor'>Find/Replace</div>"+
							"</div>"+
							"<div style='padding:5px;'>"+
							"<table border=0 cellspacing=0 cellpadding=0 style='font-size:9pt;'><tr><td style='cursor:default;'>"+
							"<span style='cursor:default;'>Find</span><br>"+
							"<input id='inpFindText_SupertreeTextEditor' type=text style=\"font-family:'宋体','Courier New';font-size:9pt;\"></input><br>"+
							"<span style='cursor:default;'>Replace with</span><br>"+
							"<input id='inpReplaceText_SupertreeTextEditor' type=text style=\"font-family:'宋体','Courier New';font-size:9pt;\"></input><br>"+
							"<input id='chkCase_SupertreeTextEditor' type='checkbox'></input><label for=chkCase_SupertreeTextEditor> match case</label><br>"+
							//"<input id='chkWord_SupertreeTextEditor' type='checkbox'></input><label for=chkWord_SupertreeTextEditor> match word</label>"+
							"</td><td valign=top style='padding-left:5px;'>"+
							"<button id='btnFindBack_SupertreeTextEditor' style=\"font-size:9pt;font-family:'宋体','Courier New';width:7em;\">Find up</button><br>"+
							"<button id='btnFind_SupertreeTextEditor' style=\"font-size:9pt;font-family:'宋体','Courier New';width:7em;\">Find down</button><br>"+
							"<button id='btnReplace_SupertreeTextEditor' style=\"font-size:9pt;font-family:'宋体','Courier New';width:7em;margin-top:5px;\">Replace</button><br>"+
							"<button style=\"font-size:9pt;font-family:'宋体','Courier New';width:7em;margin-top:5px;\" onclick=\"$esoe.$import('org.supertree.editor.TextEditor').prototype._CloseFind();\">Close</button>"+
							"</td></tr></table>"+
							"</div>";
					}
					
					obj.onmousedown=this.OnFrameBlur;
					//obj.onmouseup=this.OnFrameFocus;
					obj.onselectstart=this.PreventEventEx;
					
					$esoe.$import("org.supertree.editor.TextEditor").prototype._FindVar= this.sVarName;
					
					document.getElementById("btnFind_SupertreeTextEditor").onclick=this.OnFind;
					document.getElementById("btnFindBack_SupertreeTextEditor").onclick=this.OnFindBack;
					document.getElementById("btnReplace_SupertreeTextEditor").onclick=this.OnReplace;
					
					document.getElementById("dragFind_SupertreeTextEditor").onmousedown=this.StartDragFind;
					
					var xy= this.GetAbsXY( document.getElementById(this.sFrameID) );
					
					obj.style.left=(xy[0]+this.xyFind[0])+"px";
					obj.style.top= (xy[1]+this.xyFind[1])+"px";
					obj.style.display="block";
					obj.focus();
					
					//if selecting text
					var sel= this.GetSelection();
					if(sel)
					{
						var s=this.GetSelectionText(sel);
						if(s && s.length<100)
						{
							var objTxt=document.getElementById("inpFindText_SupertreeTextEditor");
							objTxt.value=s;
							//objTxt.focus();
							if(window.ActiveXObject) objTxt.createTextRange().select();
							else objTxt.setSelectionRange(0,s.length);
						}
					}
				},

				CalcuStringWidth_mz: function(str)
				{
					var objTmp=this.objTop.appendChild( this.getDocument().createElement("li") );
					objTmp.style.visibility="hidden";
					objTmp.innerHTML="<span></span>";
					objTmp.childNodes[0].textContent=str;
					var w= objTmp.childNodes[0].offsetWidth;
					this.objTop.removeChild(objTmp);
					return w;
				},

				ScrollX_mz: function(x)
				{
					var oWin= this.getWindow();
					if( oWin.scrollX > (x-oWin.innerWidth/2) )
					{
						this.getWindow().scrollBy(x-oWin.innerWidth/2-oWin.scrollX, 0);
					}
					else if((x+oWin.innerWidth/2)>(oWin.scrollX+oWin.innerWidth ))
					{
						this.getWindow().scrollBy(x+oWin.innerWidth/2-(oWin.scrollX+oWin.innerWidth ), 0);
					}
				},

				_OnFind: function(evt,bBack,bReplace)
				{
					var obj=document.getElementById("inpFindText_SupertreeTextEditor");
					if(!obj) return;
					var str= obj.value;
					if(!str) return;
					
					this.OnFrameFocus();
					
					var sel=this.GetSelection();
					if(!sel) return;
					
					var bCase=document.getElementById("chkCase_SupertreeTextEditor").checked;
					if(!bCase) str=str.toLowerCase();
					
					var strR= document.getElementById("inpReplaceText_SupertreeTextEditor").value;
					if( bReplace )
					{
						var sChk= this.GetSelectionText(sel);
						var sChk2= bCase?sChk:sChk.toLowerCase();
						if(sChk2==str)
						{
							var re= this._opxEx( sel, sChk, strR, 1 );
							if(re)
							{
								this.UpdateLine(sel[0]);
								
								var obje= this.objTop.childNodes[re[0]];
								var s=this.getInnerText(obje);
								this.SetSelection([sel[0],obje,sel[2],s.substring(re[1],s.length)]);
							}
							sel=this.GetSelection();
							if(!sel) return;
						}
					}
					
					//var bWord=document.getElementById("chkWord_SupertreeTextEditor").checked;
					
					//first
					var obj= bBack?sel[0]:sel[1];
					if(!obj) return;
					var s,j,s2;
					
					s= this.getInnerText(obj);
					if( bCase )
					{
						j= bBack?sel[2].lastIndexOf(str):s.indexOf(str,s.length-sel[3].length);
					}
					else
					{
						j= bBack?sel[2].toLowerCase().lastIndexOf(str):s.toLowerCase().indexOf(str,s.length-sel[3].length);
					}
					
					if( j>=0 )
					{
						this.SetSelection( [obj,obj,s.substring(0,j),s.substring(j+str.length,s.length)] );
						if(!window.ActiveXObject)
						{
							obj.scrollIntoView();
							
							var w= this.CalcuStringWidth_mz(s.substring(0,j));
							this.ScrollX_mz(w);
						}
						return;
					}
					
					while(obj=(bBack?obj.previousSibling:obj.nextSibling))
					{
						s= this.getInnerText(obj);
						
						if( bCase ) j= bBack?s.lastIndexOf(str):s.indexOf(str);
						else j= bBack?s.toLowerCase().lastIndexOf(str):s.toLowerCase().indexOf(str);
						
						if( j>=0 )
						{
							this.SetSelection( [obj,obj,s.substring(0,j),s.substring(j+str.length,s.length)] );
							if(!window.ActiveXObject)
							{
								obj.scrollIntoView();
								
								var w= this.CalcuStringWidth_mz(s.substring(0,j));
								this.ScrollX_mz(w);
							}
							return;
						}
					}
					//alert("not found");
				},

				CopyData: function()
				{
					var sel=this.GetSelection();
					if( !sel || sel[4]<1 ) return;
					
					//alert(this.GetSelectionText(sel));
					this.setClipboard(this.GetSelectionText(sel));
					
					this.Focus();
				},

				_OnCopy: function(evt)
				{
					if( this.bCopyHyperText )
					{
						this.bCopyHyperText=0;
						return true;
					}
					
					window.setTimeout( this.sVarName+".CopyData()", 0 );
					return this._PreventEvent(evt);
				},

				_OnCut: function(evt)
				{
					window.setTimeout( this.sVarName+".CutData()", 0 );
					return this._PreventEvent(evt);
				},

				SelectAll: function()
				{
					var chs= this.objTop.childNodes;
					this.SetSelection([chs[0],chs[chs.length-1],"",""]);
					
					this.Focus();
				},

				getClipboard: function()
				{
					if( window.ActiveXObject )
					{
						return window.clipboardData.getData("Text");
					}
					else
					{
						netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
						var mz= this.PrepareMz(0,0,1,0,0,1);

						mz.clip.getData(mz.trans,mz.clip.kGlobalClipboard);
						var str = new Object();
						var len = new Object();
						try
						{
							mz.trans.getTransferData('text/unicode',str,len);
						}
						catch(error){ return null;}
						
						if (str) {
							if (Components.interfaces.nsISupportsString) str=str.value.QueryInterface(Components.interfaces.nsISupportsString);
							else str = null;
						}
						if (str) return str.data;
					}
				},

				setClipboard: function(maintext)
				{
					if (window.clipboardData)
					{
						return (window.clipboardData.setData("Text", maintext));
					}
					else if (window.netscape)
					{
						netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
						var mz= this.PrepareMz(0,0,1,1,0,1);

						mz.str.data=maintext;
						mz.trans.setTransferData("text/unicode",mz.str,maintext.length*2);
						mz.clip.setData(mz.trans,null,Components.interfaces.nsIClipboard.kGlobalClipboard);
						return true;
					}
					return false;
				},

				_OnPaste: function(evt)
				{
					this.SaveSelection();	//for ie6
					window.setTimeout( this.sVarName+".PasteData()", 0 );
					return this._PreventEvent(evt);
				},

				_OnContextMenu: function(evt)
				{
					if( !evt ) evt=this.getWindow().event;
					window.setTimeout( this.sVarName+".ShowContextMenu("+evt.clientX+","+evt.clientY+")", 0 );
					return this._PreventEvent(evt);
				},

				selDrag:null,

				_OnDragStart: function(evt)
				{
					//if( !evt ) evt=window.event;
					this.selDrag=this.GetSelection();
				},

				_OnDragEnd: function(evt)
				{
					//alert("_OnDragEnd");
					this.selDrag=null;
				},

				oDragOver: null,
				objDragTmp: null,

				IsDragInSelection: function(obj,i)
				{
					if(this.selDrag[0]==this.selDrag[1])
					{
						return (obj==this.selDrag[0] && i>=this.selDrag[2].length && i<=(this.getInnerText(obj).length-this.selDrag[3].length))?true:false;
					}
					else
					{
						if( obj==this.selDrag[0] ) return (i>=this.selDrag[2].length)?true:false;
						else if( obj==this.selDrag[1] ) return (i<=(this.getInnerText(obj).length-this.selDrag[3].length))?true:false;
						else return this.getWindow().getSelection().getRangeAt(0).isPointInRange( obj,0)?true:false;
					}
				},

				_OnDragOver: function(evt)
				{
					netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
					
					var mz= this.PrepareMz(1,1);
					
					var objc= document.getElementById("dragcaret_SupertreeTextEditor");
					var obj;
					if(evt.target == objc )
					{
						if( this.oDragOver ) obj= this.oDragOver[0];
					}
					else
					{
						obj= this.FindLineObj(evt.target);
					}
					
					if(!obj || obj==this.objDragTmp)
					{
						mz.dragSession.canDrop=false;
						if(objc) objc.style.display="none";
						//window.status= evt.target;
						if( this.oDragOver ) this.oDragOver[0]=null;
					}
					else
					{
						if( !this.oDragOver ) this.oDragOver= [null,-99999,-99999];
						
						var xy= this.GetAbsXY(obj);
						var xL=xy[0],yL=xy[1];
						var dx=evt.layerX-xL;
						var dy=evt.layerY-yL;
						
						if( obj!=this.oDragOver[0] || dx!=this.oDragOver[1] || dy!=this.oDragOver[2] )
						{
							if(!this.objDragTmp)
							{
								this.objDragTmp= this.objTop.appendChild( this.getWindow().document.createElement("li") );
								this.objDragTmp.style.visibility="hidden";
								this.objDragTmp.appendChild( this.getWindow().document.createElement("span") );
								this.objDragTmp.appendChild( this.getWindow().document.createElement("span") );
								this.objDragTmp.childNodes[1].textContent="#";
							}
							var s= obj.textContent;
							var i1=0,i2=s.length,di;
							var i= parseInt( (i1+i2)/2);
							var objDragTmp=this.objDragTmp;
							var objText=objDragTmp.childNodes[0];
							var objChk=objDragTmp.childNodes[1];
							var GetAbsXY=this.GetAbsXY;
							var w= parseInt(objChk.offsetWidth);
							var h= parseInt(objChk.offsetHeight);
							xy= GetAbsXY(objDragTmp);
							var x0=xy[0],y0=xy[1];
							
							while( i!=i1 )
							{
								objText.textContent=s.substring(0,i);
								xy= GetAbsXY(objChk);
								
								//y, for line wrap
								if( (xy[1]-y0)>dy )
								{
									di=parseInt((i-i1)/2);
									if(di<1){i=i1; break;}
									i2=i;
									i=i-di;
									continue;
								}
								else if( (xy[1]+h)<dy )
								{
									di=parseInt((i2-i)/2);
									if(di<1){i=i2; break;}
									i1=i;
									i=i+di;
									continue;
								}
								
								
								//x
								if( (xy[0]-x0)>=dx )
								{
									di=parseInt((i-i1)/2);
									if(di<1){i=i1; break;}
									i2=i;
									i=i-di;
									continue;
								}
								else if( (xy[0]+w-x0)<=dx )
								{
									di=parseInt((i2-i)/2);
									if(di<1){i=i2; break;}
									i1=i;
									i=i+di;
									continue;
								}
								
								break;
							}
							
							if( this.selDrag )
							{
								if( this.IsDragInSelection(obj,i) )
								{
									mz.dragSession.canDrop=false;
									if(objc) objc.style.display="none";
									if( this.oDragOver )
									{
										this.oDragOver[0]=null;
										this.oDragOver[4]=false;
									}
									return this._PreventEvent(evt);
								}
							}
							
							mz.dragSession.canDrop=true;
							
							objText.textContent=s.substring(0,i);
							xy= GetAbsXY(objChk);
							var xy0= GetAbsXY(document.getElementById(this.sFrameID));
							
							if(!objc)
							{
								objc= document.body.appendChild(document.createElement("div"));
								objc.id="dragcaret_SupertreeTextEditor";
								objc.style.position="absolute";
								objc.style.display="none";
								objc.style.backgroundColor="blue";
								objc.style.width="1px";
								objc.style.height="1em";
								//objc.style.zIndex=1000;
								//objc.style.zIndex=90000;	//for esoe dom
								objc.style.zIndex= this.nItemZindexBase;
								
								objc.addEventListener('dragover',new Function("evt","$esoe.$import('org.supertree.editor.TextEditor').prototype.AdjustCaret(this);"),true);

							}
							objc.style.left=(parseInt(xy0[0])+parseInt(xy[0])-x0+xL+1)+"px";
							objc.style.top= (parseInt(xy0[1])+parseInt(xy[1])-y0+yL+1)+"px";
							objc.style.display="block";
							objc.setAttribute('Moved','');
							
							this.oDragOver[0]=obj;
							this.oDragOver[1]=dx;
							this.oDragOver[2]=dy;
							this.oDragOver[3]=i;
							this.oDragOver[4]=true;
							
							//window.status="drag over "+(new Date).getTime()+" "+ dx+","+dy+" "+ w+","+h+" "+ x0+","+y0+" "+i1+","+i+","+i2+" "+di+" |"+s.substring(0,i)+"|";
						}
						else mz.dragSession.canDrop=this.oDragOver[4];
					}
					
					return this._PreventEvent(evt);
				},

				PrepareMz: function(bDragService,bDragSession,bTransferable,bString,bArray,bClip)
				{
					var ret=new Object;
					if(bDragService)ret.dragService= Components.classes["@mozilla.org/widget/dragservice;1"].getService(Components.interfaces.nsIDragService);
					if(bDragSession)ret.dragSession= ret.dragService.getCurrentSession();
					if(bTransferable)
					{
						ret.trans= Components.classes["@mozilla.org/widget/transferable;1"].createInstance(Components.interfaces.nsITransferable);
						ret.trans.addDataFlavor("text/unicode");
					}
					if(bString)ret.str= Components.classes["@mozilla.org/supports-string;1"].createInstance(Components.interfaces.nsISupportsString);
					if(bArray)ret.array= Components.classes["@mozilla.org/supports-array;1"].createInstance(Components.interfaces.nsISupportsArray);
					if(bClip) ret.clip = Components.classes['@mozilla.org/widget/clipboard;1'].createInstance(Components.interfaces.nsIClipboard);
					
					return ret;
				},

				_OnDragGesture: function(evt)
				{
					this.selDrag=this.GetSelection();
					
					netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
					
					var mz= this.PrepareMz(1,0,1,1,1);
					mz.str.data = this.GetSelectionText(this.selDrag );
					mz.trans.setTransferData("text/unicode", mz.str, mz.str.data.length*2);
					mz.array.AppendElement(mz.trans);
					mz.dragService.invokeDragSession((this.selDrag[0]==this.selDrag[1])?this.selDrag[0]:this.objTop, mz.array, null, 3);	// mz.dragService.DRAGDROP_ACTION_COPY + mz.dragService.DRAGDROP_ACTION_MOVE);
					
					return this._PreventEvent(evt);
				},

				AdjustCaret: function(objCaret)
				{
					if(objCaret.getAttribute('Moved')=='1')
					{
						objCaret.style.left=(parseInt(objCaret.style.left)-1)+'px';
						objCaret.setAttribute('Moved','');
					}
					else
					{
						objCaret.style.left=(parseInt(objCaret.style.left)+1)+'px';
						objCaret.setAttribute('Moved','1');
					}
				},

				objDrop: null,
				nDropIndex: 0,
				txtDrop: null,
				nDropMode: 0,	//0: move, 1: copy

				DropData: function(evt)
				{
					if( this.txtDrop )
					{
						//alert( this.nDropMode+"\n==\n"+(this.selDrag==null) );
						//alert(this.txtDrop+"\n==\n"+this.getInnerText(this.objDrop).substring(0,this.nDropIndex));
						
						var s;
						var bM=0;
						if( this.nDropMode==0 && this.selDrag )
						{
							this._saveop(["_head"]);
							bM=1;
							
							s= this.getInnerText( this.selDrag[1] );
							if( this.objDrop==this.selDrag[1] && this.nDropIndex>=(s.length-this.selDrag[3].length) )
							{
								s=this.nDropIndex-(s.length-this.selDrag[3].length);
								this.DeleteSelection( this.selDrag, 1 );
								this.SetCaret(this.selDrag[0],this.selDrag[2].length+s);
							}
							else
							{
								this.DeleteSelection( this.selDrag, 1 );
								this.SetCaret(this.objDrop,this.nDropIndex);
							}
							this.UpdateLine(this.selDrag[0]);
							this.UpdateLine(this.selDrag[0].nextSibling);
						}
						else
						{
							this.SetCaret(this.objDrop,this.nDropIndex);
						}
						var sel= this.GetSelection();
						var re= this._opxEx( sel, "", this.txtDrop );
						
						if(bM==1) this._saveop(["_end"]);

						this.UpdateLine(sel[0]);
						//alert(selInsert[2]+"\n==\n"+selInsert[3]);
						var obje= this.objTop.childNodes[re[0]];
						s=this.getInnerText(obje);
						this.SetSelection([sel[0],obje,sel[2],s.substring(re[1],s.length)]);
						
						this.txtDrop=null;
						this.OnDragComplete(1);
					}
					
					this.PrepareCheckEx();
				},

				_OnDrop: function(evt)
				{
					if( !evt ) evt=this.getWindow().event;
					
					if( window.ActiveXObject)
					{
						this.txtDrop= evt.dataTransfer.getData("Text");
						if( this.txtDrop )
						{
							var sel=this.GetSelection();
							this.objDrop= sel[0];
							this.nDropIndex= sel[2].length;
							
							this.nDropMode= evt.ctrlKey?1:0;
							if( !this.selDrag ) this.nDropMode=1;
							//alert(this.nDropMode);
							
							window.setTimeout( this.sVarName+".DropData()", 0 );
						}
					}
					else
					{
						var sel= this.GetSelection();
						
						this.objDrop= this.oDragOver[0];
						this.nDropIndex= this.oDragOver[3];

						netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
						
						var mz= this.PrepareMz(1,1,1,0,0);
						
						if( mz.dragSession.isDataFlavorSupported("text/unicode") )
						{
							//copy or move
							this.nDropMode= evt.ctrlKey?1:0;
							if( !this.selDrag || mz.dragSession.sourceDocument!=this.getDocument() ) this.nDropMode=1;
							
							var idx=0;
							mz.dragSession.getData(mz.trans, idx);
							
							var dataObj = new Object();
							var dropSizeObj = new Object();
							mz.trans.getTransferData ("text/unicode", dataObj, dropSizeObj);
							
							var str = dataObj.value.QueryInterface(Components.interfaces.nsISupportsString);
							if( str.data )
							{
								this.txtDrop=str.data;
								window.setTimeout( this.sVarName+".DropData()", 0 );
							}
						}
						else this.txtDrop="";
						
						this.OnDragComplete();
					}
					
					return this._PreventEvent(evt);
				},

				OnDragComplete: function(bFull)
				{
					if( this.objDragTmp )
					{
						this.objTop.removeChild(this.objDragTmp);
						this.objDragTmp=null;
					}
					
					var objc= document.getElementById("dragcaret_SupertreeTextEditor");
					if( objc && objc.style.display!="none" ) objc.style.display="none";
					
					if(bFull && this.selDrag) this.selDrag= null;
				},

				CancelOperate: function()
				{
					if( !this.nDockMenu || ((new Date).getTime()-this.nDockMenu)>2000) $esoe.$import("org.supertree.editor.TextEditor").prototype.HideContextMenu();
					this.OnDragComplete(1);
				},

				_OnClick: function(evt)
				{
					this.CancelOperate();
					this.OnSelectionChange();
				},

				selSave: null,

				SaveSelection: function()
				{
					this.CheckChange();
					
					var sel=this.GetSelection();
					if(sel)
					{
						this.selSave=sel;
						//this.TRACE("save sel, "+ sel[4]);
					}
				},

				RestoreSelection: function()
				{
					if( this.selSave )
					{
						//alert("h");
						this.SetSelection(this.selSave);
						//this.selSave=null;
						//this.TRACE("restore sel, "+ this.selSave[4]);
					}
					/*
					this.SetSelection(this.selLast);
					*/
				},

				_OnFrameFocus: function(evt)
				{
					if( !evt ) evt=window.event;
					if(!this.GetSelection()) this.RestoreSelection();
					this.Focus();
					if(evt) return this._PreventEvent(evt);
				},

				_OnFrameBlur: function(evt)
				{
					this.CheckChange();

					if( !evt ) evt=window.event;
					this.SaveSelection();
					//alert(this.selSave==null);
					//this.CancelOperate();
					//return this._PreventEvent(evt)
					if(window.ActiveXObject) return this._PreventEvent(evt);
				},

				_OnBlur: function(evt)
				{
					this.CheckChange();

					//if( !evt ) evt=this.getWindow().event;
					window.setTimeout( this.sVarName+".CancelOperate()",500);
					//return this._OnFrameBlur(evt)
					return this._PreventEvent(evt);
				},

				ToggleBookmark: function()
				{
					var sel=this.GetSelection();
					if(!sel)return;
					
					sel[0].style.backgroundColor=(sel[0].style.backgroundColor)?"":"lavender";
				},

				NextBookmark: function(bBack)
				{
					var sel=this.GetSelection();
					if(!sel)return;
					var obj=sel[0];
					while(obj=bBack?obj.previousSibling:obj.nextSibling)
					{
						if( obj.style.backgroundColor )
						{
							this.SetCaret(obj,0);
							if(!window.ActiveXObject) obj.scrollIntoView();
							return;
						}
					}
				},

				ToggleLineNumer: function()
				{
					this.bLineNumber=this.bLineNumber?0:1;
					this.UpdateLineNumber(1);
				},

				ToggleWrap: function()
				{
					if( this.objTop.style.wordWrap=="break-word" )
					{
						this.objTop.style.wordWrap="";
						this.objTop.style.whiteSpace="nowrap";
					}
					else
					{
						this.objTop.style.wordWrap="break-word";
						this.objTop.style.whiteSpace="";
					}
				},

				bCopyHyperText: 0,

				CopyHyperText: function()
				{
					if(!this.GetSelection()) this.RestoreSelection();
					
					if( window.ActiveXObject )
					{
						this.bCopyHyperText=1;
					}
					else
					{
						netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
					}
					this.getDocument().execCommand("copy",false,null);
				},

				nDockMenu: 0,

				ShowMenuTool: function()
				{
					var objBtn=document.getElementById( "xy_"+this.sFrameID ).previousSibling;
					var xy=this.GetAbsXY(objBtn);
					this.ShowContextMenu(0,0,1);
					var obj= document.getElementById("ctxmenu_SupertreeTextEditor");
					
					var sel= this.GetSelection();
					
					var sa=new Array;
					sa[sa.length]=this.ContextMenuItem( "Jump... (Ctrl+G)", 0, this.sVarName+".HideContextMenu();"+this.sVarName+".GotoLine();" );
					sa[sa.length]=this.ContextMenuItem( "Toggle Line Numer", 0, this.sVarName+".HideContextMenu();"+this.sVarName+".ToggleLineNumer();" );
					
					if(window.ActiveXObject) sa[sa.length]=this.ContextMenuItem( "Toggle Wrap", 0, this.sVarName+".HideContextMenu();"+this.sVarName+".ToggleWrap();" );

					sa[sa.length]=this.ContextMenuItem( "Copy Hypertext", (sel && sel[4])?0:1 , this.sVarName+".HideContextMenu();"+this.sVarName+".CopyHyperText();" );
					sa[sa.length]=this.ContextMenuItem( "Check Stack", sel?0:1 , this.sVarName+".HideContextMenu();"+this.sVarName+".CheckStack();", 0, "Check line-end syntax stack, for debugging syntax rule." );

					var imax= this.aRule.length;
					var i,s;
					if(imax>1)
					{
						for(i=0;i<imax;i+=3)
						{
							if( this.ruleTop==this.aRule[i] ) s= "<span style='font-weight:bold;'>"+this.aRule[i+1]+"</span>";
							else s=this.aRule[i+1];
							
							sa[sa.length]=this.ContextMenuItem( s, 0, this.sVarName+".HideContextMenu();"+this.sVarName+".SetRule("+i+");",(i==0)?1:0, "Use "+this.aRule[i+1]+" syntax rule" );
						}
					}
					
					//sa[sa.length]=this.ContextMenuItem( "Insert Local Time", sel?0:1 , this.sVarName+".HideContextMenu();"+this.sVarName+".InsertText((new Date).toLocaleString());",1 );
					//sa[sa.length]=this.ContextMenuItem( "Insert UTC Time", sel?0:1 , this.sVarName+".HideContextMenu();"+this.sVarName+".InsertText((new Date).toString());" );
					//sa[sa.length]=this.ContextMenuItem( "Insert UTC Timestamp", sel?0:1 , this.sVarName+".HideContextMenu();"+this.sVarName+".InsertText("+this.sVarName+".GetTimestamp());" );
					
					sa[sa.length]="<table border=0 cellspacing=0 cellpadding=0 style='border-top:1px solid lightgrey;font-size:9pt;'><tr><td style='padding-left:5px;cursor:default;'><nobr>Time:</nobr></td><td style='padding-left:5px;'>";
					sa[sa.length]=this.ContextMenuItem( "Local", sel?0:1 , this.sVarName+".HideContextMenu();"+this.sVarName+".InsertText((new Date).toLocaleString());",0,"Insert local time",1 );
					sa[sa.length]="</td><td style='padding-left:5px;'>";
					sa[sa.length]=this.ContextMenuItem( "UTC", sel?0:1 , this.sVarName+".HideContextMenu();"+this.sVarName+".InsertText((new Date).toString());",0,"Insert UTC time",1 );
					sa[sa.length]="</td><td style='padding-left:5px;'>";
					sa[sa.length]=this.ContextMenuItem( "UTC+0", sel?0:1 , this.sVarName+".HideContextMenu();"+this.sVarName+".InsertText((new Date).toUTCString());",0,"Insert UTC+00:00 time",1 );
					sa[sa.length]="</td><td style='padding-left:5px;'>";
					sa[sa.length]=this.ContextMenuItem( "Timestamp", sel?0:1 , this.sVarName+".HideContextMenu();"+this.sVarName+".InsertText("+this.sVarName+".GetTimestamp());",0,"Insert UTC timestamp",1 );
					sa[sa.length]="</td></tr></table>";
					
					if( this.aRuleInsert===0 )
					{
						//un-touched
						imax=this.aRule.length;
						for(i=0;i<imax;i+=3)
						{
							if( this.ruleTop==this.aRule[i] )
							{
								this.aRuleInsert=this.aRule[i+2];
								break;
							}
						}
					}
					
					if( this.aRuleInsert && this.aRuleInsert.length>0 )
					{
						imax=this.aRuleInsert.length;
						for(i=0;i<imax;i+=2)
						{
							if(!this.aRuleInsert[i] || !this.aRuleInsert[i+1] ) break;
							sa[sa.length]=this.ContextMenuItem( this.aRuleInsert[i], sel?0:1 , this.sVarName+".HideContextMenu();"+this.sVarName+".InsertText("+this.sVarName+".aRuleInsert["+(i+1)+"]);" );
						}
					}
					
					sa[sa.length]=this.ContextMenuItem( "Help", 0, this.sVarName+".HideContextMenu();"+this.sVarName+".ShowHelp();" , 1);
					sa[sa.length]=this.ContextMenuItem( "About", 0, this.sVarName+".HideContextMenu();"+this.sVarName+".ShowAbout();" );
					
					obj.innerHTML=sa.join("");
					
					this.nDockMenu=(new Date).getTime();
					
					obj.style.width="16em";

					obj.style.left=(xy[0])+"px";
					obj.style.top= (xy[1]+objBtn.offsetHeight)+"px";
					//obj.style.top= (xy[1])+"px";
					obj.style.display="block";
					
				},

				CheckStack: function()
				{
					var sel=this.GetSelection();
					if(!sel || !sel[0] ) return;
					alert(sel[0].getAttribute("Stack").substring(0,1000));
				},

				ShowAbout: function()
				{
					alert("Supertree Text Editor V1.0\n"+
						"\n"+
						"Author: Feng WeiGuo (冯卫国)\n"+
						"\n"+
						"Web: www.supertree.org\n"+
						"     www.viviasoft.com\n"+
						"Email: forxm@163.com\n"+
						"\n"+
						"Shenzhen, Guangdong, China.\n"+
						"2008-1\n"+
						"\n"+
						"Visit http://www.supertree.org for more information."
						);
				},

				ShowHelp: function()
				{
					alert("Supertree Text Editor Help, v1.0\n"+
						"\n"+
						"-- Tested on IE6 and Firefox2, P4 1.6G, 256M --\n"+
						"\n"+
						"[Main Function]\n"+
						"\n"+
						"Cut, Copy, Paste, Drag & Drop, Undo, Redo, Find, Replace, Bookmark, Jump to Line,\n"+
						"Toggle Line Number, Toggle Text Wrap (IE Only),\n"+
						"Insert Time and Pre-defined Text.\n"+
						"\n"+
						"[Shortcut Keys]\n"+
						"\n"+
						"Ctrl+X, Ctrl+C, Ctrl+V, Ctrl+Drag & Drop, Ctrl+Z, Ctrl+Y, Ctrl+A, Ctrl+F, F3, Shift+F3, Ctrl+F2, F2, Shift+F2, Ctrl+G, "+
						"Ctrl+Del, Ctrl+Backspace, Tab (4 spaces), Shift+Tab, F1.\n"+
						"\n"+
						"[Other]\n"+
						"\n"+
						"All Tabs (\\u0009) will convert to 4 spaces (\\u0020).\n"+
						"All unicode spaces (\\u00A0) will convert to ascii spaces (\\u0020).\n"+
						"Use Del to delete single space, use Backspace to delete Tabs (4 spaces).\n"+
						"Syntax analysis for a line will stop if the line contains more than 2000 characters.\n"+
						"\n"+
						"[Developer]\n"+
						"\n"+
						"SetText()/GetText() interface.\n"+
						"Support self-defined syntax rule.\n"+
						"\n"+
						"Visit http://www.supertree.org for more information."
						);
				},

				GetTimestamp: function()
				{
					var dt=new Date;
					var tz=-dt.getTimezoneOffset();
					if(tz!=0) tz=((tz<0)?"":"+")+Math.floor(tz/60)+":"+(tz%60);
					else tz="";
					
					return (dt.getFullYear()+"-"+(dt.getMonth()+1)+"-"+dt.getDate()+" "+dt.getHours()+":"+dt.getMinutes()+":"+dt.getSeconds()+" UTC"+tz).replace( /\b(\d)\b/g , "0$1");
				},

				InsertText: function(s)
				{
					var sel=this.GetSelection();
					if(!sel || !s) return;
					
					if(s.indexOf("\n")>=0)
					{
						var sTab= sel[2].replace( this.regHeadSpace , "$1" );
						
						if( sTab.length>3)
						{
							if(sTab==sel[2] && s.charAt(0)=="\n" ) s=s.substring(1,s.length);
							
							sTab=sTab.substring(0,sTab.length-(sTab.length%4));
							s=s.replace( /\n/g, "\n"+sTab );
						}
						
					}
					
					var re= this._opxEx(sel,this.GetSelectionText(sel),s);
					
					if(re)
					{
						sel[0].setAttribute("Stack","");
						
						this.UpdateLine(sel[0]);
						
						var obje= this.objTop.childNodes[re[0]];
						//var s=this.getInnerText(obje);
						//this.SetSelection([sel[0],obje,sel[2],s.substring(re[1],s.length)]);
						this.SetCaret(obje,re[1]);
					}
				},

				aRuleInsert: 0,

				SetRule: function(idx)
				{
					var rule= this.aRule[idx];
					if(rule)
					{
						this.ruleTop= rule;
					}
					this.aRuleInsert= this.aRule[idx+2];
					
					this.aStkCache1=null;
					this.aStkCache2=null;
					
					var sel=this.GetSelection();
					var obj=this.objTop.firstChild;
					if(obj)
					{
						obj.setAttribute("Stack","");
						while(obj=obj.nextSibling) obj.setAttribute("Stack","");
					}
					this.UpdateLine(this.objTop.firstChild);
					if(sel)this.SetSelection(sel);
				},
				
				SetRuleEx: function(sName)
				{
					var i,imax= this.aRule.length;
					for(i=0;i<imax;i+=3)
					{
						if( this.aRule[i+1].toLowerCase()==sName.toLowerCase() )
						{
							this.SetRule( i );
							return;
						}
					}
				},
				
				objXY: null,

				//sThisPath: "",

				ToolItem: function(nIcon,sTitle,sEval,bSel)
				{
					return "<td><div "+
						"style='background-image:url(TextEditor.gif);width:16px;height:16px;background-position:-"+16*nIcon+"px 0px;position:relative;padding-right:1px;' "+
						"title='"+sTitle+"' "+
						"onmouseover=\"this.style.left='-1px';this.style.top='-1px';\" "+
				//		"onmouseup=\"this.style.left='-1px';this.style.top='-1px';"+this.sVarName+".RestoreSelection();\" "+
				//		"onmouseup=\"this.style.left='-1px';this.style.top='-1px';\" "+
						"onmouseout=\"this.style.left='0px';this.style.top='0px';\" "+
				//		"onmousedown=\""+this.sVarName+".SaveSelection();this.style.left='0px';this.style.top='0px';"+(sEval?sEval:"")+";\" "+
				//		"onmousedown=\"this.style.left='0px';this.style.top='0px';\" "+
						"onmousedown=\"this.style.left='0px';this.style.top='0px';"+this.sVarName+".SaveSelection();"+this.sVarName+".RestoreSelection();return "+this.sVarName+"._PreventEventEx(arguments[0]);\" "+
						//"onclick=\""+(bSel?("if("+this.sVarName+".GetSelection())"):"")+"{"+(sEval?sEval:"")+";}\" "+
						"onmouseup=\"this.style.left='-1px';this.style.top='-1px';"+this.sVarName+".RestoreSelection();"+(bSel?("if("+this.sVarName+".GetSelection())"):"")+"{"+(sEval?sEval:"")+";};return "+this.sVarName+"._PreventEventEx(arguments[0]);\" "+
						
				//		"onmousedown=\"this.style.left='0px';this.style.top='0px';"+this.sVarName+".OnFrameBlur();"+(bSel?("if("+this.sVarName+".GetSelection())"):"")+"{"+(sEval?sEval:"")+";}\" "+
						"></div></td>";
				},

				//objArea: id or obj, for ie, to prevent focus/selection lost
				LoadTool: function(idTool,objArea,sOpenEval,sSaveEval)
				{
					var obj;
					
					/*
					if(!this.sThisPath )
					{
						obj= document.getElementById("_SUPERTREETEXTEDITOR_");
						//alert(obj.src);
						var s= obj.src.replace( /([\\\/]|^)[^\\\/]*$/g, "$1" );
						this.sThisPath=s?s:"./";
						//alert(this.sThisPath);
					}
					*/
					
					obj= document.getElementById(idTool);
					
					var sa= new Array;
					sa[sa.length]="<table border='0' cellpadding='0' cellspacing='0' style='font-size:1px;'><tr>";
					
					if( sOpenEval ) sa[sa.length]=this.ToolItem(13,"Open", sOpenEval );
					if( sSaveEval ) sa[sa.length]=this.ToolItem(0,"Save",sSaveEval).replace( /^<td/, "<td name=save ");
					
					sa[sa.length]=this.ToolItem(1,"Cut (Ctrl+X)",this.sVarName+".CutData()",1);
					sa[sa.length]=this.ToolItem(2,"Copy (Ctrl+C)",this.sVarName+".CopyData()",1);
					sa[sa.length]=this.ToolItem(3,"Paste (Ctrl+V)",this.sVarName+".PasteData()",1);
					sa[sa.length]=this.ToolItem(4,"Undo (Ctrl+Z)",this.sVarName+".Undo()");
					sa[sa.length]=this.ToolItem(5,"Redo (Ctrl+Y)",this.sVarName+".Redo()");
					sa[sa.length]=this.ToolItem(6,"Find (Ctrl+F)",this.sVarName+".Find()");
					sa[sa.length]=this.ToolItem(7,"Find Next (F3)",this.sVarName+"._OnFind()");
					sa[sa.length]=this.ToolItem(8,"Find Previous (Shift+F3)",this.sVarName+"._OnFind(null,1)");
					sa[sa.length]=this.ToolItem(9,"Toggle Bookmark (Ctrl+F2)",this.sVarName+".ToggleBookmark()");
					sa[sa.length]=this.ToolItem(10,"Next Bookmark (F2)",this.sVarName+".NextBookmark()");
					sa[sa.length]=this.ToolItem(11,"Previous Bookmark (Shift+F2)",this.sVarName+".NextBookmark(1)");
					sa[sa.length]=this.ToolItem(12,"Menu/Tool",this.sVarName+".ShowMenuTool()");
					sa[sa.length]="<td id='xy_"+this.sFrameID+"' style='font-size:8pt;padding-left:2em;color:darkgray;'></td>";
					sa[sa.length]="</tr></table>";
					
					obj.innerHTML= sa.join("");
					
					this.objXY=document.getElementById("xy_"+this.sFrameID);
					
					if(window.ActiveXObject )
					{
						if( objArea )
						{
							obj= (typeof(objArea)=="string")?document.getElementById(objArea):objArea;
						}
						//obj.onmousedown=new Function("","alert('h2')");
						//obj.onmousedown=this.OnFrameBlur;
						//obj.onmouseup=this.OnFrameFocus;
						obj.attachEvent( "onmousedown", this.OnFrameBlur );
						obj.attachEvent( "onmouseup", this.OnFrameFocus );
					}
				},

				bMouseDown: 0,

				_OnMouseDown: function(evt)
				{
					this.bMouseDown=1;
				},

				_OnMouseUp: function(evt)
				{
					this.bMouseDown=0;
				},

				_OnMouseMove: function(evt)
				{
					if(this.bMouseDown) this._OnSelectionChange();
				},

				//passive change checking, for ime mode and other uncatchable change
				objCheckTop: null,		//0 for firstChild
				objCheckBottom: null,
				aCheckText: null,
				nCheckLineNum: 0,

				IsCheckValid: function()
				{
					if( this.objCheckTop!==0 && (!this.objCheckTop || this.objCheckTop.parentNode!=this.objTop) )
					{
						//this.TRACE("invalid check 1:" +(!this.objCheckTop) );
						return false;
					}
					if( this.objCheckBottom && this.objCheckBottom.parentNode!=this.objTop )
					{
						//this.TRACE("invalid check 2");
						return false;
					}
					return true;
				},

				PrepareCheckEx: function()
				{
					var sel=this.GetSelection();
					if(!sel) return null;
					
					var objTop=sel[0].previousSibling;		//for ff2.0 bug(?), top= prev 1 line
					if(!objTop) objTop=0;
					
					var objBottom= sel[1].nextSibling;
					if( objBottom ) objBottom= objBottom.nextSibling;	//for ie6 bug(?), bottom= next 2 line
					
					this.PrepareCheck(objTop,objBottom);
					
					return sel;
				},

				PrepareCheck: function(objTop,objBottom)
				{
					//try
					{
						if(!this.aCheckText) this.aCheckText= new Array;
						else this.aCheckText.splice(0,this.aCheckText.length);
						
						/*
						//if( !objTop && objTop!==0 ) alert("null");
						var ob1=objTop?objTop:((objTop===0)?this.objTop.firstChild:null);
						var ob2=objBottom?objBottom:this.objTop.lastChild;
						if(!ob1)alert("ob1 fail:"+ob1);
						if(!ob2)alert("ob2 fail");
						*/
						
						this.GetSelectionText( [objTop?objTop:((objTop===0)?this.objTop.firstChild:null),objBottom?objBottom:this.objTop.lastChild,"",""], this.aCheckText );
						this.objCheckTop=objTop;
						this.objCheckBottom=objBottom;
						
						this.nCheckLineNum= this.objTop.childNodes.length;
					}
					/*
					catch(e)
					{
						this.objChekcTop=null;
						alert("prepare passive checking error: " + e + ","+e.description  );
					}
					*/
				},

				ResetCheck: function()
				{
					this.objCheckTop= null;
					this.CheckChange();
				},

				CheckChange: function()
				{
					var sel;
					if( !this.IsCheckValid() )
					{
						//this.TRACE("invalid check");
						this.objCheckTop= null;
					}
					else
					{
						//alert("h");
						/*
						if( this.nCheckLineNum==this.objTop.childNodes.length )
						{
							var s=this.getInnerText(this.objCheckTop);
							var dif= this.FindDiff(this.aCheckText[0],s);
							if(dif)
							{
								//alert("change");
								sel=this.GetSelection();
								this._op(this._line(this.objCheckTop),dif[0],this.aCheckText[0].substring(dif[0],this.aCheckText[0].length-dif[1]),s.substring(dif[0],s.length-dif[1]),0,2,"Inp");
								this.PrepareCheck(this.objCheckTop,this.objCheckBottom);	//keep check range
								this.UpdateLine(this.objCheckTop);
								if(sel)this.SetSelection(sel);
							}
						}
						else //if( this.nCheckLineNum>this.objTop.childNodes.length )
						*/
						{
							var objTop= this.objCheckTop;
							if(objTop===0) objTop=this.objTop.firstChild;
							
							var sa= new Array;
							this.GetSelectionText( [objTop,this.objCheckBottom?this.objCheckBottom:this.objTop.lastChild,"",""], sa );
							
							var dif= this.FindDiffEx(this.aCheckText,sa);
							//this.TRACE(sa.join("|"));
							if(dif)
							{
								//alert("change2");
								//sel=this.GetSelection();
								this._op(this._line(objTop)+dif[0],dif[1],dif[4],dif[5],0,2);
								this.PrepareCheck(this.objCheckTop,this.objCheckBottom);	//keep check range
								//this.UpdateLine(this.objCheckTop?objTop.nextSibling:objTop);
								//if(sel)this.SetSelection(sel);
							}
						}
					}
				},

				_OnSelectionChange: function(evt)
				{
					this.CheckChange();
					
					var sel=this.PrepareCheckEx();
					
					this.UpdateXY(sel);
				},

				//show x,y
				UpdateXY: function(sel)
				{
					if(!this.objXY) return;
					
					if(!sel) sel= this.GetSelection();
					if(!sel) return;
					
					var j= this._line(sel[0]);
					this.objXY.innerHTML=((j<0)?"?":(j+1))+","+(sel[2].length+1);
				},

				_lineCache:-1,

				_line: function(obj)
				{
					if(obj.parentNode!=this.objTop ) return -1;
					
					var chs= this.objTop.childNodes;
					if( this._lineCache>=0 && this._lineCache<chs.length )
					{
						var objCache= chs[this._lineCache];
						if( obj==objCache ) return this._lineCache;
						if( obj.nextSibling==objCache) return this._lineCache=this._lineCache-1;
						if( obj.previousSibling==objCache) return this._lineCache=this._lineCache+1;
					}
					//this.TRACE("calcu");
					
					var y0=obj.offsetTop;
					var dy=obj.offsetHeight;
					
					var y1,obj1,i1=0;
					if((y1=(obj1=chs[i1]).offsetTop)==y0 ) return this._lineCache=i1;
					var y2,obj2,i2=chs.length-1;
					if((y2=(obj2=chs[i2]).offsetTop)==y0 ) return this._lineCache=i2;
					
					var j,obji,im,ym;
					var k=0;
					//var s="";
					while(k++<20)
					{
						im= Math.floor((i1+i2)/2);
						if(im==i1||im==i2) return -1;
						if((ym=(obji=chs[im]).offsetTop)==y0 )
						{
							//this.TRACE(k+":1:"+s);
							return this._lineCache=im;
						}
						
						if(ym<y0) { i1=im;y1=ym;obj1=obji; }
						else { i2=im;y2=ym;obj2=obji; }

						if((y0-y1)<(y2-y0))
						{
							j= i1+Math.floor((y0-y1)/obj1.offsetHeight);
							if(j>=i2) j=i2-1;
							
							//s+=",("+i1+","+j+")";
						}
						else
						{
							j= i2-Math.floor((y2-y0)/dy);
							if(j<i1) j=i1+1;
							
							//s+=",("+i2+","+j+")";
						}
						
						if((ym=(obji=chs[j]).offsetTop)==y0 )
						{
							//this.TRACE(k+":2:"+s);
							return this._lineCache=j;
						}
						
						if( ym<y0 )
						{
							if( (y1=(obj1=obji.nextSibling).offsetTop)==y0 )
							{
								//this.TRACE(k+":3:"+s);
								return this._lineCache=j+1;
							}
							i1=j+1;
						}
						else
						{
							if( (y2=(obj2=obji.previousSibling).offsetTop)==y0 )
							{
								//this.TRACE(k+":4:"+s);
								return this._lineCache=j-1;
							}
							i2=j-1;
						}
					}
					//this.TRACE(k+":4:"+s);
					return -1;
				},

				//static, each rule contain 3 item: [rule_data,name,aInsert]
				aRule: new Array,

				/*
				//static
				AddRule: function( rule, name, aInsert )
				{
					this.aRule[this.aRule.length]=rule;
					this.aRule[this.aRule.length]=name;
					this.aRule[this.aRule.length]=aInsert?aInsert:null;
				},
				*/
				
				//static
				AddRule: function( oRule, sNewName )
				{
					var i,imax= this.aRule.length;
					for(i=0;i<imax;i+=3)
					{
						if( this.aRule[i]===oRule.rule ) return;
					}
					
					this.aRule[this.aRule.length]=oRule.rule;
					this.aRule[this.aRule.length]=sNewName?sNewName:(oRule.sName?oRule.sName:"[unnamed rule]");
					this.aRule[this.aRule.length]=oRule.aInsert?oRule.aInsert:null;
				},

				OnInitViewer_base: $esoe.$import( "org.supertree.editor.TextParser" ).prototype.OnInitViewer,

				OnInitViewer: function()
				{
					//alert("init");
					this.OnInitViewer_base();
					
					if( this.bReadOnly ) return;
					
					var oDoc= this.getDocument();
					
					if( window.ActiveXObject )
					{
						//oDoc.onkeydown= this.OnKeyDown;
						oDoc.attachEvent( "onkeydown", this.OnKeyDown );
						
						//oDoc.onkeypress= this.OnKeyPress;
						oDoc.attachEvent( "onkeypress", this.OnKeyPress );
						
						oDoc.body.onpaste=this.OnPaste;
						oDoc.body.oncut=this.OnCut;
						oDoc.body.oncopy=this.OnCopy;
						
						oDoc.body.ondragstart=this.OnDragStart;
						//oDoc.body.ondragend=this.OnDragEnd;
						oDoc.body.ondrop=this.OnDrop;
						
						//document.getElementById(this.sFrameID).onfocus=this.OnFrameFocus;
						//document.getElementById(this.sFrameID).onblur=this.OnFrameBlur;
						//oDoc.body.onfocus=this.OnFrameFocus;
						
						//oDoc.body.onblur=this.OnBlur;		//for hide context menu
						oDoc.body.attachEvent( "onblur", this.OnBlur );
						
						//oDoc.onbeforeeditfocus=this.OnBeforeEditFocus;
						
						//oDoc.body.oncontextmenu=this.OnContextMenu;
						oDoc.body.attachEvent( "oncontextmenu", this.OnContextMenu );
						
						//oDoc.body.onclick=this.OnClick;
						oDoc.body.attachEvent( "onclick", this.OnClick );
						
						//oDoc.onselectionchange= this.OnSelectionChange;
						oDoc.attachEvent( "onselectionchange", this.OnSelectionChange );
					}
					else
					{
						oDoc.designMode= 'on';
						
						oDoc.addEventListener('keydown',this.OnKeyDown,false);
						oDoc.addEventListener('keypress',this.OnKeyPress,false);
						
						oDoc.addEventListener('contextmenu',this.OnContextMenu,false);
						oDoc.addEventListener('click',this.OnClick,false);
						
						//oDoc.addEventListener('cut',this.OnCut,false);
						/*
						oDoc.body.addEventListener('draggesture',OnVsEditorDragStart,false);
						oDoc.body.addEventListener('dragdrop',OnVsEditorDrop,true);
						*/
						oDoc.body.addEventListener('draggesture',this.OnDragGesture,true);

						oDoc.body.addEventListener('dragdrop',this.OnDrop,true);
						oDoc.addEventListener('dragover',this.OnDragOver,true);
						oDoc.addEventListener('dragenter',this.OnDragOver,true);
						
						oDoc.body.addEventListener('mousemove',this.OnMouseMove,true);
						oDoc.body.addEventListener('mousedown',this.OnMouseDown,true);
						oDoc.body.addEventListener('mouseup',this.OnMouseUp,true);
						
						oDoc.addEventListener('blur',this.OnBlur,true);
						
						//oDoc.body.addEventListener('dragenter',this.OnDragOver,true);
						//oDoc.body.addEventListener('drag',this.OnDragOver,true);
					}

				}
			}
			);
		this.prototype.regHeadSpace.compile( "^(\\s*)($|\\S.*$)", "" );
		
		this.prototype.AddRule(
			{
				rule:this.prototype.ruleDefault,
				sName: "Text"
			}
			);
		
		//#ESOE_RES
		//#FILE: file_TextEditor_gif, TextEditor.gif
		//#END_ESOE_RES

	}
	);
